// UsersService.cs
// Facebook/Framework/Service
// Copyright (c) 2007, Nikhil Kothari. All Rights Reserved.
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Facebook.Service.Core;

namespace Facebook.Service {

    /// <summary>
    /// Represents the service APIs that allow access to information on Facebook users.
    /// </summary>
    public sealed class UsersService {

        private FacebookSession _session;

        internal UsersService(FacebookSession session) {
            Debug.Assert(session != null);
            _session = session;
        }

        /// <summary>
        /// Gets the desired information about the specified user ID. This corresponds to the
        /// users.getInfo Facebook method.
        /// </summary>
        /// <param name="userID">The requested user ID, or null to get information about the current user.</param>
        /// <param name="fields">The comma-separated list of fields, or null to get all known fields.</param>
        /// <returns>The information about the specific user; null if the call fails.</returns>
        public User GetUser(string userID, string fields) {
            if (String.IsNullOrEmpty(userID)) {
                userID = _session.UserID;
            }
            if (String.IsNullOrEmpty(fields)) {
                fields = User.AllFields;
            }

            FacebookRequest request = new FacebookRequest(_session);
            request.Parameters["uids"] = userID;
            request.Parameters["fields"] = fields;

            FacebookResponse response = request.InvokeMethod("users.getInfo");
            if (response.Status == FacebookResponseStatus.Succeeded) {
                if (response.Result is ArrayList) {
                    Debug.Assert(response.Result is ArrayList);
                    ArrayList userInfoList = (ArrayList)response.Result;

                    Debug.Assert(userInfoList.Count == 1);
                    Debug.Assert(userInfoList[0] is Hashtable);
                    return new User((Hashtable)userInfoList[0]);
                }
                else if (response.Result is Hashtable) {
                    return new User((Hashtable)response.Result);
                }
            }

            return null;
        }

        /// <summary>
        /// Gets the desired information about the specified user IDs. This corresponds to the
        /// users.getInfo Facebook method.
        /// </summary>
        /// <param name="userIDs">The list of requested user IDs.</param>
        /// <param name="fields">The comma-separated list of fields, or null to get all known fields.</param>
        /// <returns>The information about the specified users; null if the call fails.</returns>
        public ICollection<User> GetUsers(ICollection<string> userIDs, string fields) {
            if ((userIDs == null) || (userIDs.Count == 0)) {
                throw new ArgumentNullException("userIDs");
            }
            if (String.IsNullOrEmpty(fields)) {
                fields = User.AllFields;
            }

            StringBuilder uidBuilder = new StringBuilder();
            foreach (string id in userIDs) {
                if (uidBuilder.Length != 0) {
                    uidBuilder.Append(",");
                }
                uidBuilder.Append(id);
            }

            FacebookRequest request = new FacebookRequest(_session);
            request.Parameters["uids"] = uidBuilder.ToString();
            request.Parameters["fields"] = fields;

            FacebookResponse response = request.InvokeMethod("users.getInfo");
            if (response.Status == FacebookResponseStatus.Succeeded) {
                List<User> users;

                if (response.Result is ArrayList) {
                    ArrayList userInfoList = (ArrayList)response.Result;

                    users = new List<User>(userInfoList.Count);
                    foreach (Hashtable userState in userInfoList) {
                        users.Add(new User((Hashtable)userState));
                    }
                }
                else if (response.Result is Hashtable) {
                    users = new List<User>(1);
                    users.Add(new User((Hashtable)response.Result));
                }
                else {
                    users = new List<User>(0);
                }

                return users;
            }

            return null;
        }

        /// <summary>
        /// Checks if the current user is the administrator of the current page
        /// that the application is running within.
        /// </summary>
        /// <returns>true if the user is an admin; false otherwise.</returns>
        public bool IsPageAdmin() {
            if (_session.SessionType != FacebookSessionType.PresencePage) {
                throw new InvalidOperationException("The current application is not running on a Facebook page.");
            }

            return IsPageAdmin(_session.UserID);
        }

        /// <summary>
        /// Checks if the current user is the administrator of the specified page.
        /// </summary>
        /// <param name="pageID">The ID of the specified page.</param>
        /// <returns>true if the user is an admin; false otherwise.</returns>
        public bool IsPageAdmin(string pageID) {
            if (String.IsNullOrEmpty(pageID)) {
                throw new ArgumentNullException("pageID");
            }

            FacebookRequest request = new FacebookRequest(_session);
            request.Parameters["page_id"] = pageID;

            FacebookResponse response = request.InvokeMethod("pages.isAdmin");
            if (response.Status == FacebookResponseStatus.Succeeded) {
                return FacebookResponse.CoerceBoolean(response.Result);
            }

            return false;
        }

        /// <summary>
        /// Checks if the current user is a fan of the specified page.
        /// </summary>
        /// <param name="pageID">The ID of the specified page.</param>
        /// <returns>true if the user is a fan; false otherwise.</returns>
        public bool IsPageFan(string pageID) {
            return IsPageFan(pageID, null);
        }

        /// <summary>
        /// Checks if the specified user is a fan of the specified page.
        /// </summary>
        /// <param name="pageID">The ID of the specified page.</param>
        /// <param name="userID">The ID of the specified user.</param>
        /// <returns>true if the user is a fan; false otherwise.</returns>
        /// <returns></returns>
        public bool IsPageFan(string pageID, string userID) {
            if (String.IsNullOrEmpty(pageID)) {
                throw new ArgumentNullException("pageID");
            }

            if (String.IsNullOrEmpty(userID)) {
                if (_session.SessionType == FacebookSessionType.User) {
                    userID = _session.UserID;
                }
                else {
                    userID = _session.PageUserID;
                }
            }

            FacebookRequest request = new FacebookRequest(_session);
            request.Parameters["page_id"] = pageID;
            request.Parameters["uid"] = userID;

            FacebookResponse response = request.InvokeMethod("pages.isFan");
            if (response.Status == FacebookResponseStatus.Succeeded) {
                return FacebookResponse.CoerceBoolean(response.Result);
            }

            return false;
        }

        /// <summary>
        /// Sets the current user's status message. If the specified status is
        /// empty, the status message is cleared instead.
        /// </summary>
        /// <param name="status">The message to set.</param>
        /// <returns>true if the message was set; false otherwise.</returns>
        public bool SetStatus(string status) {
            bool dummy;
            return SetStatus(status, /* includesVerb */ false, out dummy);
        }

        /// <summary>
        /// Sets the current user's status message. If the specified status is
        /// empty, the status message is cleared instead.
        /// The value of includesVerb determines if Facebook should prefix the status
        /// with the leading "is" or not.
        /// </summary>
        /// <param name="status">The message to set.</param>
        /// <param name="includesVerb">True if the status includes the verb and the default 'is' should be disabled; false otherwise.</param>
        /// <returns>true if the message was set; false otherwise.</returns>
        public bool SetStatus(string status, bool includesVerb) {
            bool dummy;
            return SetStatus(status, includesVerb, out dummy);
        }

        /// <summary>
        /// Sets the current user's status message. If the specified status is
        /// empty, the status message is cleared instead.
        /// </summary>
        /// <param name="status">The message to set.</param>
        /// <param name="permissionNotGranted">Indicates whether the application does not have permissions to set the status.</param>
        /// <returns>true if the message was set; false otherwise.</returns>
        [Obsolete("Use the SetStatus overload with the includesVerb parameter")]
        public bool SetStatus(string status, out bool permissionNotGranted) {
            return SetStatus(status, /* includesVerb */ false, out permissionNotGranted);
        }

        /// <summary>
        /// Sets the current user's status message. If the specified status is
        /// empty, the status message is cleared instead.
        /// The value of includesVerb determines if Facebook should prefix the status
        /// with the leading "is" or not.
        /// </summary>
        /// <param name="status">The message to set.</param>
        /// <param name="includesVerb">True if the status includes the verb and the default 'is' should be disabled; false otherwise.</param>
        /// <param name="permissionNotGranted">Indicates whether the application does not have permissions to set the status.</param>
        /// <returns>true if the message was set; false otherwise.</returns>
        public bool SetStatus(string status, bool includesVerb, out bool permissionNotGranted) {
            permissionNotGranted = false;

            FacebookRequest request = new FacebookRequest(_session);
            if (String.IsNullOrEmpty(status)) {
                request.Parameters["clear"] = "1";
            }
            else {
                request.Parameters["status"] = status;
            }
            if (includesVerb) {
                request.Parameters["status_includes_verb"] = "1";
            }

            FacebookResponse response = request.InvokeMethod("users.setStatus");
            if (response.Status == FacebookResponseStatus.PermissionNotGranted) {
                permissionNotGranted = true;
                return false;
            }
            else if (response.Status == FacebookResponseStatus.Succeeded) {
                Debug.Assert(response.Result is bool);
                return (bool)response.Result;
            }

            return false;
        }
    }
}
